package com.mark.aspect;

import com.mark.common.exception.DataException;
import com.mark.web.data.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.validation.BeanPropertyBindingResult;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description: 日志切面
 * @Author: Mark
 * @CreateDate: 2019/8/25 17:18
 * @Version: 3.00.001
 * @Copyright : 豆浆油条个人非正式工作室
 *
 * https://www.cnblogs.com/cjsblog/p/9111975.html
 *
 */
@Aspect
@Component
@Slf4j
public class LogAspect {

    ThreadLocal<Long> costTime = new ThreadLocal<>();

    /**
     *
     */
    @Pointcut("execution(public * com.mark.web..controller..*Controller.*(..))")
    public void pointcut() {

    }

    @Around("pointcut()")
    public Object doInvoke(ProceedingJoinPoint joinPoint) {
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            if (throwable instanceof DataException) {
                //如果是自定义的异常则继续向上级抛出，交给全局异常处理
                throw new DataException(throwable.getMessage());
            } else {
                throwable.printStackTrace();
                log.error(throwable.getMessage(), throwable);
                throw new RuntimeException(throwable);
            }

        } finally {
            long endTime = System.currentTimeMillis();
            long costTime = endTime - startTime;
            printLog(joinPoint, result, costTime);
        }
        return result;
    }

    private void printLog(ProceedingJoinPoint joinPoint, Object result, long costTime) {
        LogInfo logInfo = parseLogInfo(joinPoint);
        if(null != logInfo){
            logInfo.setResult(JsonUtil.obj2String(result));
            logInfo.setElapsedTime(costTime);
            log.info(logInfo.format(),logInfo.args());
        }
    }

    private LogInfo parseLogInfo(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String className = signature.getDeclaringTypeName();
        String methodName = signature.getName();
        Object[] args = joinPoint.getArgs();
        String targetClassName = joinPoint.getTarget().getClass().getName();
        try {
            //反射获取目标类
            Class<?> aClass = Class.forName(targetClassName);
            Method[] methods = aClass.getMethods();
            for(Method method : methods){
                //当方法名和参数个数相同时
                if (method.getName().equals(methodName) && method.getParameterCount() == args.length) {
                    LogInfo logInfo = new LogInfo();
                    logInfo.setClassName(className);
                    logInfo.setMethodName(methodName);
                    //取出BindingResult参数，如果需要的话；因为BindingResult表单校验构建的参数不需要打印的log中，同时避免转json的时候出现出现异常
                    args = delBindingResultArgsIfNecessary(args);
                    logInfo.setArguments(JsonUtil.obj2String(args));
                    return logInfo;
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            log.error(e.getMessage(),e);
        }
        return null;
    }

    private Object[] delBindingResultArgsIfNecessary(Object[] args) {
        return Arrays.stream(args).filter(arg -> !(arg instanceof BeanPropertyBindingResult)).toArray();
    }

}
